package com.idea.relax.log.appender.file;

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import com.idea.relax.log.appender.ILogbackAppender;
import com.idea.relax.log.constant.LogConstant;
import com.idea.relax.log.constant.PropsConstant;
import com.idea.relax.log.props.LogLevel;
import com.idea.relax.log.props.RelaxLogProperties;
import com.idea.relax.log.support.utils.*;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders;
import net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;


import static org.springframework.boot.logging.LoggingSystemProperties.FILE_LOG_PATTERN;

/**
 * @author L.cm
 * @author salad(参考自L.cm的Mica)
 * @date: 2022/12/17
 * @description: File日志的appender
 */
@Slf4j
public class LoggingFileJsonAppender implements ILogbackAppender {

    /**
     * 文件日志的配置信息
     */
    private final RelaxLogProperties.FileLog props;

    /**
     * 日志文件生成的父级路径
     */
    private final String logSecLevelDir;

    /**
     * 有参构造器中初始化文件日志的相关信息
     *
     * @param environment Environment对象
     * @param properties  RelaxLogProperties对象
     */
    public LoggingFileJsonAppender(Environment environment,
                                   RelaxLogProperties properties) {
        this.props = properties.getFileLog();
        String appName = environment.getRequiredProperty(PropsConstant.APP_NAME_KEY);
        String fileLogPattern = environment.resolvePlaceholders(LoggingUtil.DEFAULT_FILE_LOG_PATTERN);
        System.setProperty(FILE_LOG_PATTERN, fileLogPattern);
        String logDir = environment.getProperty("logging.file.path", LoggingUtil.DEFAULT_LOG_DIR);
        this.logSecLevelDir = logDir.concat(StringPool.SLASH).concat(appName).concat(StringPool.SLASH);
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        this.start(context);
    }

    @Override
    public void start(LoggerContext context) {
        log.info("File logging start.");
        apply(context);
    }

    @Override
    public void reset(LoggerContext context) {
        log.info("File logging reset.");
        apply(context);
    }

    @Override
    public void apply(LoggerContext context) {
        //是否开启了FileAppender
        if (props.isEnabled()) {
            if (Func.isEmpty(props.getLogLevels())) {
                addLevelFileJsonAppender(context, logSecLevelDir.concat(LoggingUtil.LOG_FILE_INFO), props, LogLevel.ALL);
            } else {
                for (LogLevel level : props.getLogLevels()) {
                    String fileName = level.getLevel().levelStr.toLowerCase().concat(".log");
                    String logFile = logSecLevelDir.concat(fileName);
                    addLevelFileJsonAppender(context, logFile, props, level);
                }
            }
            log.info("File logging apply successfully.");
        }
    }

    /**
     * 增加FileAppender
     * @param context LoggerContext
     * @param logFile 日志存储路径
     * @param props 配置信息
     * @param level 日志等级
     */
    private void addLevelFileJsonAppender(LoggerContext context, String logFile, RelaxLogProperties.FileLog props, LogLevel level) {
        // More documentation is available at: https://github.com/logstash/logstash-logback-encoder
        final RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
        String name = StringUtil.isBlank(props.getAppenderNamePrefix()) ? "FILE_"
                : props.getAppenderNamePrefix() + level.getLevel().levelStr;
        fileAppender.setContext(context);
        fileAppender.addFilter(LoggingUtil.levelFilter(context, level));
        fileAppender.setEncoder(compositeJsonEncoder(context));
        fileAppender.setName(name);
        fileAppender.setFile(logFile);
        fileAppender.setRollingPolicy(LoggingUtil.rollingPolicy(context, fileAppender, logFile));
        if (props.isAsyncAppender()) {
            AsyncAppender asyncAppender = LoggingUtil.bindAsyncAppender(fileAppender, 512, 0, false);
            asyncAppender.start();
        } else {
            fileAppender.start();
        }
        decideApplyAppender(context, name, fileAppender);
    }


    /**
     * 日志Json格式的设置
     * @param context LoggerContext
     * @return LoggingEventCompositeJsonEncoder
     */
    private LoggingEventCompositeJsonEncoder compositeJsonEncoder(LoggerContext context) {
        final LoggingEventCompositeJsonEncoder compositeJsonEncoder = new LoggingEventCompositeJsonEncoder();
        compositeJsonEncoder.setContext(context);
        LoggingEventJsonProviders loggingEventJsonProviders = LogStashUtil.jsonProviders(context);
        loggingEventJsonProviders.addStackHash(LogStashUtil.stackHashJsonProvider("stackHash", LogConstant.EXCLUSION_PATTERNS));
        compositeJsonEncoder.setProviders(loggingEventJsonProviders);
        compositeJsonEncoder.start();
        return compositeJsonEncoder;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

}
